package com.ndkj.rpcserver.proxy;

import com.ndkj.rpcserver.common.dto.Invocation;
import com.ndkj.rpcserver.common.dto.URL;
import com.ndkj.rpcserver.common.utils.HttpClient;
import com.ndkj.rpcserver.loadbalance.LoadBalanceRule;
import com.ndkj.rpcserver.proxy.chain.MethodMockChain;
import com.ndkj.rpcserver.proxy.chain.RpcInvocationChain;
import com.ndkj.rpcserver.register.MapRemoteRegister;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;

@SuppressWarnings("all")
public class ProxyFactory {

    /**
     * 通过代理对象来进行简单RPC调用
     */
    public static <T> T getProxy(Class interfaceClazz, String version) {
        Object proxyInstance = Proxy.newProxyInstance(interfaceClazz.getClassLoader(), new Class[]{interfaceClazz}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 代理信息
                Class<?>[] parameterTypes = method.getParameterTypes();
                Invocation invocationDTO = new Invocation(interfaceClazz.getName(), method.getName(), version, parameterTypes, args);

                // 服务发现
                List<URL> prividerHosts = MapRemoteRegister.get(interfaceClazz.getName(), version);

                // 负载均衡
                URL provider = LoadBalanceRule.randomRule(prividerHosts);

                // 服务调用
                // 发送调用请求至目标机器，由目标机器上的LocalRegister本地缓存上注册的接口实现类执行（目标机器上进行泛化调用）
                HttpClient httpClient = new HttpClient();
                return httpClient.send(provider.getHost(), provider.getPort(), invocationDTO);
            }
        });
        return (T) proxyInstance;
    }

    /**
     * 通过代理对象来进行调用（支持服务容错和服务Mock）
     */
    public static <T> T getMockAndTolerantProxy(Class interfaceClazz, String version) {
        Object proxyInstance = Proxy.newProxyInstance(interfaceClazz.getClassLoader(), new Class[]{interfaceClazz}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                ProxyChainHandler chainHandler = new ProxyChainHandler();
                // Mock Chain
                chainHandler.addHandler(new MethodMockChain());
                // RpcInvocation Chain
                chainHandler.addHandler(new RpcInvocationChain());
                Object result = chainHandler.process(interfaceClazz, version, proxy, method, args);
                return result;
            }
        });
        return (T) proxyInstance;
    }

}
